package org.batatasframework.expression;

import java.util.HashMap;
import java.util.Map;

import org.batatasframework.values.AbstractValueReader;
import org.batatasframework.values.StringValueGetter;
import org.springframework.util.Assert;

import cn.bonoon.kernel.util.StringHelper;

/**
 * 通用的指令表达式，通常是读取配置文件里的一行
 * 如：
 * name = -f my -m 100
 * 
 * @author jackson
 *
 */
public class CommandExpression extends AbstractValueReader{
	
	protected static final CommandExpressionArgument EMPTY = new CommandExpressionArgument();
	protected static final String COMMAND_ARGUMENT_SEPARATOR = "\\s";
	
	/**
	 * 这里是解析出来的指令的参数和对应的值，
	 * 如：
	 * name = -f my -m 100
	 * 那么这里存储的就是
	 * -f : "my"
	 * -m : 100
	 */
	protected final Map<String, CommandExpressionArgument> values = new HashMap<>();
	protected final String[] args;

	public CommandExpression(StringValueGetter eValue, String... argNames) throws Exception{
		this(eValue.getString().split(COMMAND_ARGUMENT_SEPARATOR), argNames);
	}
	
	public CommandExpression(ExpressionClause eClause, String... argNames) throws Exception{
		this(eClause.getString().split(COMMAND_ARGUMENT_SEPARATOR), argNames);
	}
	
	public CommandExpression(String[] args, String... argNames) throws Exception{
		this.args = args;
		
		if(null != argNames){
			argument(argNames);
		}
	}
	
	/** 取得一个参数的值，如果没有定义这个参数，则会返回{@link #EMPTY}对象 */
	@Override
	public CommandExpressionArgument getValue(String argName){
		if(argName.charAt(0) == '.' && argName.length() > 1) argName = argName.substring(1);
		
		CommandExpressionArgument cei = values.get(argName);
		if(null == cei){ cei = EMPTY; }
		return cei;
	}
	
	public void single(String name, String value) throws Exception{
		Assert.isNull(values.put(name, new CommandExpressionArgument(name, value)), 
				"已经存在指令的同名参数，不允许定义两个相同的参数：" + name);
	}
	
	/**
	 * <pre>
	 * 解析普通的参数，即如：-file /a/b/c
	 * 即指令名，后面空格跟着的是参数
	 * 也是构造函数默认的使用
	 * 
	 * 特征：一个参数名、一个参数值，占用两个变量
	 * 
	 * 注意，占用两个变量的应该比占用一个变量的先被处理，这样是为了避免参数值变量被当成了
	 * 一个变量指令进行解析
	 * </pre>
	 */
	protected CommandExpression argument(String... argNames) throws Exception{
		for(String argName : argNames){
			for(int i = 0; i < args.length; i++){
				if(!argName.equalsIgnoreCase(args[i])){ continue; }
				
				args[i++] = null;
				String argValue = null;
				if(i < args.length){
					argValue = args[i];
					args[i] = null;
				}
				
				single(argName, argValue);
				break;
			}
		}
		return this;
	}
	
	/**
	 * <pre>
	 * 解析语句的片断，即参数后带值，没有分开的情况，如：.2表示小数点后取2位小数
	 * 
	 * 注意，这个应该在{@link #argument(String...)}方法之后被调用，可以避免参数没有值的情况
	 * 
	 * 特征：参数名与参数值组合在一起，占用一个变量
	 * </pre>
	 */
	public CommandExpression fragments(String...fragments) throws Exception{
		for(String fragment : fragments){
			String lcFra = fragment.toLowerCase();
			for(int i = 0; i < args.length; i++){
				String arg = args[i];
				if(StringHelper.isNotEmpty(arg) && arg.toLowerCase().startsWith(lcFra)){
					single(fragment, arg.substring(fragment.length()));
					args[i] = null;
					break;
				}
			}
		}
		return this;
	}
	
	/**
	 * <pre>
	 * 解析标识符，如：readonly等。。。
	 * 
	 * 注意与{@link #argument(String...)}和{@link #fragments(String...)}的区别
	 * 
	 * 特征：标识符，结果boolean值，表示为是否定义了这个标识符，占一个变量
	 * </pre>
	 */
	public CommandExpression identitfies(String...identitfies) throws Exception{
		for(String identitfie : identitfies){
			boolean hasMatch = false;
			for(int i = 0; i < args.length; i++){
				String arg = args[i];
				if(StringHelper.isNotEmpty(arg) && identitfie.startsWith(arg)){
					hasMatch = checkMatch(hasMatch, arg);
					single(identitfie, "true");
					args[i] = null;
				}
			}
		}
		return this;
	}
	
	private boolean checkMatch(boolean match, String val) throws Exception{
		if(match) throw new Exception("存在有歧义的定义：" + val);
		return true;
	}
	
	/**
	 * <pre>
	 * 是否直接在表达式里定义了一个枚举的情况
	 * 
	 * 注意：枚举类型的解析应该在指令类型的后面被解析
	 * </pre>
	 */
	public <E extends Enum<E>> E toEnum(Class<E> clazz, E defaultValue){
		for(int i = 0; i < args.length; i++){
			String arg = args[i];
			if(StringHelper.isEmpty(arg)) continue;
			try{
				E result = Enum.valueOf(clazz, arg.toUpperCase());
				args[i] = null;
				return result;
			}catch (Exception e) {
				//nothing to do and try next
			}
		}
		return defaultValue;
	}

	/** 参考：{@link #toEnum(Class, Enum)} */
	public <E extends Enum<E>> E toEnum(Class<E> clazz){
		return toEnum(clazz, null);
	}

	/** 表达式里是否存在值与"arg"的值相同 */
	public boolean match(String arg){
		if(null != arg){
			for(String a : args){
				if(arg.equals(a)) return true;
			}
		}
		return false;
	}
	
	public <T> T match(Map<String, T> mapped){
		for(String a : args){
			if(null != a){
				T t = mapped.get(a);
				if(null != t) return t;
			}
		}
		return null;
	}
}
